interface Person {
  name: string;
  age?: number;
  sex: boolean;
}

{
  // Parameters 用于提取函数类型的参数类型

  type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any
    ? P
    : never;

  type res = Parameters<(a: number, b: string) => void>;

  function hello(this: Person, name?: string) {
    console.log(this.name);
  }

  type res2 = Parameters<typeof hello>;
}

// -----------------------------------------------------------------

{
  // ReturnType 用于提取函数类型的返回值类型

  type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R
    ? R
    : never;

  type res = ReturnType<(a: number, b: string) => string>;
}

// -----------------------------------------------------------------

{
  // ConstructorParameters
  // 构造函数 和 函数类型的区别就是可以被new
  // Parameters 用于提取函数参数的类型，而ConstructorParameters用于提取构造器参数的类型
  // prettier-ignore
  type ConstructorParameters<
    T extends abstract new (...args: any) => any
  > =
    T extends abstract new (...args: infer P) => any 
    ? P 
    : never;

  /*
      类型参数 T 是待处理的类型，通过 extends 约束为构造器类型，加个 abstract 代表不能直接被实例化（其实不加也行）。
      用 T 匹配一个模式类型，提取参数的部分到 infer 声明的局部变量 P 里，返回 P。
    */

  interface PersonConstructor {
    new (name: string): Person;
  }

  type res = ConstructorParameters<PersonConstructor>;
}

// -----------------------------------------------------------------

{
  // InstanceType 提取构造器的返回值类型

  type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (
    ...args: any
  ) => infer R
    ? R
    : never;

  interface PersonConstructor {
    new (name: string): Person;
  }

  type res = InstanceType<PersonConstructor>;
}

// -----------------------------------------------------------------

{
  // ThisParameterType 提取this类型

  function hello(this: Person) {
    console.log(this.name);
  }

  type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown;

  type res = ThisParameterType<typeof hello>;
}

{
  // OmitThisParameter
  // 提取出 this 的类型之后，自然可以构造一个新的，比如删除 this 的类型可以用 OmitThisParameter。

  function hello(this: Person, name?: string) {
    console.log(this.name);
  }

  // 直接使用typeof 获取一个值函数类型时 如果函数包含this的指定 其返回值也会包含进去
  // 其OmitThisParameter做的事就是当函数指定this时，去除this，返回函数的类型
  type res = typeof hello;

  // prettier-ignore
  type OmitThisParameter<T> =
    // 先看一下 泛型是否指定了其this类型，如果this的类型时unknown或any 则直接返回泛型（函数类型）
    unknown extends ThisParameterType<T>
      ? T
      // 如果存在this类型 则重新构造出一个新类型
      : T extends (...args: infer A) => infer R
        ? (...args: A) => R
        : T

  type res2 = OmitThisParameter<typeof hello>;

  /*
    类型参数 T 为待处理的类型。

    用 ThisParameterType 提取 T 的 this 类型，如果提取出来的类型是 unknown 或者 any，
    那么 unknown extends ThisParameterType 就成立，也就是没有指定 this 的类型，所以直接返回 T。

    否则，就通过模式匹配提取参数和返回值的类型到 infer 声明的局部变量 A 和 R 中，用它们构造新的函数类型返回。
  */
}

// -----------------------------------------------------------------

{
  // Partial 将接口类型中的各个类型变为可选类型

  type Partial<T> = {
    [K in keyof T]?: T[K];
  };

  type res = Partial<Person>;
}

// -----------------------------------------------------------------

{
  // Required 将接口类型中的各个类型变为必选类型

  type Required<T> = {
    [K in keyof T]-?: T[K];
  };

  type res = Required<Person>;
}

// -----------------------------------------------------------------

{
  // Readonly 将接口类型中的各个类型变为只读类型

  type Readonly<T> = {
    readonly [K in keyof T]: T[K];
  };

  type res = Readonly<Person>;
}

// -----------------------------------------------------------------

{
  // Pick 根据传递的 K 构造出一个新的类型

  type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
  };

  type res = Pick<Person, 'age' | 'name'>;
}

// -----------------------------------------------------------------

{
  // Record 用于创建索引类型，传入key和值类型
  // keyof any === string | number | symbol

  type Record<K extends keyof any, V> = {
    [P in K]: V;
  };

  // 当传入的 K 是 string | number | symbol，那么创建的就是有可索引签名的索引类型：
  type res = Record<string, string>;

  // type res2 = {
  //   a: string;
  //   b: string;
  // };
  type res2 = Record<'a' | 'b', string>;
}

// -----------------------------------------------------------------

{
  // Exclude: 当想从一个联合类型中去掉一部分类型时，可以用 Exclude 类型：

  type Exclude<T, U> = T extends U ? never : T;

  // type res = "a" | "c"
  type res = Exclude<'a' | 'b' | 'c', 'b' | 'd'>;

  /*
    联合类型当作为类型参数出现在条件类型左边时，会被分散成单个类型传入，这叫做分布式条件类型。
    所以写法上可以简化， T extends U 就是对每个类型的判断。
    过滤掉 U 类型，剩下的类型组成联合类型。也就是取差集。
  */
}

// -----------------------------------------------------------------

{
  // Extract: 与Exclude功能相反，保留指定类型 取交集

  type Extract<T, U> = T extends U ? T : never;

  // type res = 'b' | 'c';
  type res = Extract<'a' | 'b' | 'c', 'b' | 'c' | 'd'>;
}

// -----------------------------------------------------------------

{
  // Omit
  // 我们知道了 Pick 可以取出索引类型的一部分索引构造成新的索引类型，那反过来就是去掉这部分索引构造成新的索引类型。

  type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

  type res = Omit<Person, 'age' | 'name'>;
}

// -----------------------------------------------------------------

{
  // 在递归那节我们写过取 Promise 的 ValuType 的高级类型，这个比较常用，ts 也给内置了，就是 Awaited。
  // 它的实现比我们当时写的完善一些：

  // prettier-ignore
  type Awaited<T> = 
    T extends null | undefined 
      ? T
      : T extends object & { then(onfulfilled: infer F): any }
        ? F extends ((value: infer V, ...args: any) => any)
          ? Awaited<V>
          : never
        : T

  type res = Awaited<Promise<Promise<Promise<number>>>>;
}

// -----------------------------------------------------------------

{
  // NotNullable
  // 就是用于判断是否为非空类型，也就是不是 null 或者 undefined 的类型的，实现比较简单：

  type NotNullable<T> = T extends null | undefined ? never : T;
}

// -----------------------------------------------------------------
